# Serene Programming Language
#
# Copyright (c) 2019-2024 Sameer Rahmani <lxsameer@gnu.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
{
  description = "Serene programming language";

  #inputs.nixpkgs.url = "github:NixOS/nixpkgs/bcb1a9c7e1d8568c5e58316fe3254eb8f4455439";
  inputs.nixpkgs.url = "github:lxsameer/nixpkgs/e1f7865bce4d52d30dd1d61e79798ee2765cc2b0";
  #inputs.nixpkgs.url = "/home/lxsameer/src/nixpkgs/";
  inputs.flake-utils.url = "github:numtide/flake-utils";

  outputs = inputs@{ self, nixpkgs, flake-utils, ... }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        # Out zsh configuration directory. mkShell will pick up the .zshrc
        # from this directory.
        zshDir = ./scripts;

        # Most of these overlays are do to bugs and problems
        # in upstream nixpkgs. But thanks to their design
        # We can fix them using these overlays and contribuete
        # them upstream little by little.
        overlays = [
          (final: prev: {
            p11-kit = prev.p11-kit.overrideAttrs (old: {
              patches = [
                ./nix/patches/p11-kit_skip_test.patch
              ];
            });

            cpio = prev.cpio.overrideAttrs (old: {
              nativeBuildInputs = [ prev.autoreconfHook ];
              NIX_CFLAGS_COMPILE = "-Wno-implicit-function-declaration";
            });

            libedit = prev.libedit.overrideAttrs (old: {
              # Musl is ISO 10646 compliant but doesn't define __STDC_ISO_10646__ we need to do it ourselves
              NIX_CFLAGS_COMPILE = "-D__STDC_ISO_10646__=201103L";
            });

            elfutils = prev.elfutils.overrideAttrs (old: {
              # libcxx does not have __cxa_demangle
              configureFlags = old.configureFlags ++ [ "--disable-demangler" ];
            });

            ccache = prev.ccache.overrideAttrs (old: {
              nativeBuildInputs = old.nativeBuildInputs ++ [ final.elfutils ];
            });

            # We don't need systemd at all
            util-linux = prev.util-linux.override { systemdSupport = false; };

            # libpam exmaples use glibc. We need to disable them
            linux-pam = prev.linux-pam.overrideAttrs (old: {
              postConfigure = ''
                sed 's/examples//' -i Makefile
              '';
            });

            #=============================================================
            # Since we're using lld-17, and --no-undefined-version is the
            # default in lld-17. We need to explicitely turn it off for
            # these problematic packages untill they fix it upstream.
            libgcrypt = prev.libgcrypt.overrideAttrs (old: {
              NIX_LDFLAGS = if prev.stdenv.cc.isClang
                            then [ "--undefined-version" ]
                            else [];
            });
            libxcrypt = prev.libxcrypt.overrideAttrs (old: {
              NIX_LDFLAGS = if prev.stdenv.cc.isClang
                            then [ "--undefined-version" ]
                            else [];
            });
            ncurses = prev.ncurses.overrideAttrs (old: {
              NIX_LDFLAGS = if prev.stdenv.cc.isClang
                            then [ "--undefined-version" ]
                            else [];
            });

             libbsd = prev.libbsd.overrideAttrs (old: { #old.NIX_LDFLAGS ++
              NIX_LDFLAGS = if prev.stdenv.cc.isClang
                            then [ "--undefined-version" ]
                            else [];
            });
            libidn2 = prev.libidn2.overrideAttrs (old: { #old.NIX_LDFLAGS ++
              NIX_LDFLAGS = if prev.stdenv.cc.isClang
                            then [ "--undefined-version" ]
                            else [];
            });
            #==============================================================

            iwyu = (prev.include-what-you-use.overrideAttrs (old:
              let
                version = "0.21";
              in {
                inherit version;

                src = prev.fetchurl {
                  url = "${old.meta.homepage}/downloads/${old.pname}-${version}.src.tar.gz";
                  hash = "sha256-ajUZGf+JvafJXIlUcmAYaNs9qrlqlYs44DYokNWHYLY=";
                };
                cmakeFlags = [ "-DCMAKE_PREFIX_PATH=${prev.llvmPackages_17.llvm.dev}" ];

              })).override {
                llvmPackages = prev.__splicedPackages.llvmPackages_17;
              };
          })
        ];


        utils = import ./nix/utils.nix { inherit nixpkgs; };

        # Create a package set based on the build system
        pkgs = utils.get_pkgs system overlays;

        nativePkgs = import nixpkgs { inherit system overlays; };

        # Create a stdenv based on LLVM 17
        stdenv = pkgs.stdenvAdapters.overrideCC pkgs.stdenv pkgs.llvmPackages_17.clangUseLLVM;

        gc = pkgs.callPackage ./nix/boehmgc.nix { inherit stdenv; };

        zlib' = pkgs.zlib-ng.overrideAttrs (old: {
          cmakeFlags = [
            "-DCMAKE_INSTALL_PREFIX=/"
            "-DBUILD_SHARED_LIBS=OFF"
            "-DINSTALL_UTILS=ON"
            "-DZLIB_COMPAT=ON"
          ];
        });

        # By default llvm adds zlib to `propagatedBuildInputs` which means any
        # package that uses llvm will indirectly depends on zlib. And since
        # by default that zlib is built as a shared lib (since our packageset
        # is not static), We can't statically link to it. So, we just replace
        # that zlib with our override of zlib-ng
        clang' = stdenv.cc.overrideAttrs (old: {
          propagatedBuildInputs = [ stdenv.cc.bintools ] ++ [ zlib' ];
        });
        llvm = pkgs.llvmPackages_17.llvm.overrideAttrs (old: {
          propagatedBuildInputs = [ zlib' ];
        });

        # This is the actual stdenv that we need to use anywhere else
        stdenv' = pkgs.stdenvAdapters.overrideCC pkgs.stdenv clang';

        # Just disabling the tests that fails under musl
        git' = pkgs.git.overrideAttrs (old: {
          preInstallCheck =
            pkgs.lib.replaceStrings [ ''disable_test t0201-gettext-fallbacks'' ]
              [ ''
    disable_test t0201-gettext-fallbacks
    disable_test t2082-parallel-checkout-attributes
    '' ]
              old.preInstallCheck;
        });

        buildToolsDeps = (with pkgs; [
            cmake
            ninja
            llvm
            llvmPackages_17.mlir
            llvmPackages_17.clang
            iwyu
        ]);

        shellTools = (with pkgs; [
          zsh
          zsh-autosuggestions
          zsh-autocomplete
          zsh-syntax-highlighting
        ]);

        buildDevToolsDeps = (with pkgs; [
          ccache
          git'
          python3
        ]);

        buildDeps = (with pkgs; [
          gc
          zlib'
          llvm
          llvmPackages_17.mlir
          llvmPackages_17.clang
        ]);

        testDeps = (with pkgs; [
          gtest
          gmock
          gbenchmark
        ]);



        tex = nativePkgs.texlive.combine {
          inherit (nativePkgs.texlive)
            scheme-minimal
            latex-bin
            latexmk
            xelatex-dev
            sourcesanspro
            xkeyval
            etoolbox
            titlesec
            fontspec
            hyperref
            mfirstuc
            parskip
            geometry
            pdftexcmds
            infwarerr
            kvoptions
          ;
        };


        mathDeps = (with nativePkgs; [
          idris2
          agda
          git
          zsh
          zsh-autosuggestions
          zsh-autocomplete
          zsh-syntax-highlighting
          tex
        ]);


      in {
        inherit pkgs;
        devShells.default = (pkgs.mkShell.override { stdenv = stdenv';}) {
          nativeBuildInputs = buildDevToolsDeps ++ buildToolsDeps ++ shellTools;
          buildInputs = buildDeps ++ testDeps;
          shellHook =
            ''
              BUILDER= ZDOTDIR=${zshDir} zsh -d && exit
            '';
        };

        # This shell is gcc based and we use it only
        # for the mathematics side of our design
        devShells.math = nativePkgs.mkShell {
          #nativeBuildInputs = mathDeps;
          buildInputs = mathDeps;
          shellHook =
            ''
              BUILDER=Math ZDOTDIR=${zshDir} zsh -d && exit
            '';
        };

        #packages.llvm = llvm;
      }
    );
}
